// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_WM_GESTURES_BACK_GESTURE_BACK_GESTURE_EVENT_HANDLER_H_
#define ASH_WM_GESTURES_BACK_GESTURE_BACK_GESTURE_EVENT_HANDLER_H_

#include "ash/wm/gestures/back_gesture/back_gesture_metrics.h"
#include "base/containers/flat_set.h"
#include "ui/display/display_observer.h"
#include "ui/events/event_handler.h"
#include "ui/events/gestures/gesture_provider_aura.h"

namespace ash {

class BackGestureAffordance;
class BackGestureContextualNudgeControllerImpl;

class BackGestureEventHandler : public display::DisplayObserver,
                                public ui::EventHandler,
                                public ui::GestureConsumer,
                                public ui::GestureProviderAuraClient {
 public:
  // The threshold of the fling velocity while fling from left edge to go
  // previous page.
  static constexpr int kFlingVelocityForGoingBack = 1000;

  // How many dips are reserved for gesture events to start swiping to previous
  // page from the left edge of the screen in tablet mode.
  static constexpr int kStartGoingBackLeftEdgeInset = 16;

  BackGestureEventHandler();
  BackGestureEventHandler(const BackGestureEventHandler&) = delete;
  BackGestureEventHandler& operator=(const BackGestureEventHandler&) = delete;
  ~BackGestureEventHandler() override;

  // display::DisplayObserver:
  void OnDisplayMetricsChanged(const display::Display& display,
                               uint32_t metrics) override;

  // ui::EventHandler:
  void OnGestureEvent(ui::GestureEvent* event) override;
  void OnTouchEvent(ui::TouchEvent* event) override;

  // ui::GestureProviderAuraClient:
  void OnGestureEvent(GestureConsumer* consumer,
                      ui::GestureEvent* event) override;

  BackGestureContextualNudgeControllerImpl* nudge_controller_for_testing() {
    return nudge_controller_.get();
  }

 private:
  // Returns true if |event| was handled as a go-back gesture. |event| is
  // generated by |gesture_provider_| from touch event, |screen_location| is
  // location of the event in screen coordinates.
  bool MaybeHandleBackGesture(ui::GestureEvent* event,
                              const gfx::Point& screen_location);

  // True if we can start swiping from left edge of the display or splitview
  // divider to go back.
  bool CanStartGoingBack(const gfx::Point& screen_location);

  void SendBackEvent(const gfx::Point& screen_location);

  // Returns true if we should wait for touch press ack to decide whether to
  // show back gesture. If true, BackGestureEventHandler should not handle touch
  // press event in OnTouchEvent() but should wait after touch ack has been
  // received.
  bool ShouldWaitForTouchPressAck(const gfx::Point& screen_location);

  // True if swiping from left edge to go to previous page is in progress.
  bool going_back_started_ = false;

  // Tracks the x-axis and y-axis drag amount through touch events. Used for
  // back gesture affordance in tablet mode. The gesture movement of back
  // gesture can't be recognized by GestureRecognizer, which leads to wrong
  // gesture locations of back gesture. See crbug.com/1015464 for the details.
  int x_drag_amount_ = 0;
  int y_drag_amount_ = 0;

  // True if back gesture dragging on the negative direction of x-axis.
  bool during_reverse_dragging_ = false;

  // Position of last touch event. Used to calculate |y_drag_amount_|. Note,
  // only touch events from |first_touch_id_| will be recorded.
  gfx::Point last_touch_point_;
  ui::PointerId first_touch_id_ = ui::kPointerIdUnknown;

  // Maintains the ids list of the touch events that are not generated from
  // |first_touch_id_| when back gesture is performed. We need this so that we
  // can ignore gesture events that are not generated from the first finger.
  base::flat_set<uint32_t> other_touch_event_ids_list_;

  // Used to show the affordance while swiping from left edge to go to the
  // previous page.
  std::unique_ptr<BackGestureAffordance> back_gesture_affordance_;

  // Used to decide when to show/hide the back gesture contextual nudge.
  std::unique_ptr<BackGestureContextualNudgeControllerImpl> nudge_controller_;

  // True if back gesture dragged from splitview divider.
  bool dragged_from_splitview_divider_ = false;

  // Start location of the back gesture in screen coordinate. Used by
  // ActivateUnderneathWindowInSplitViewMode() to determine the snapped window
  // that should be activated for going back.
  gfx::Point back_start_location_;

  // A GestureProvider that is created for back gesture. Used to handle tap down
  // and the possibly following gesture scroll sequence for back gesture in
  // OnTouchEvent session. This is done to avoid tap down event be used by the
  // window that is underneath to do other things (e.g, highlight a menu item)
  // instead of going back.
  ui::GestureProviderAura gesture_provider_{this, this};

  // Register for DisplayObserver callbacks.
  display::ScopedDisplayObserver display_observer_{this};

  // False if BackGestureEventHandler should not handle touch events directly in
  // OnTouchEvent(), but should wait after touch ack is received. This is needed
  // as the window's touch action (if exist) will only be set after it sees the
  // touch start event and we'll need the touch action information to decide
  // whether back gesture should be shown.
  bool should_wait_for_touch_ack_ = false;

  // Start scenario type of the back gesture, used for related metrics.
  BackGestureStartScenarioType back_gesture_start_scenario_type_ =
      BackGestureStartScenarioType::kMaxValue;
};

}  // namespace ash

#endif  // ASH_WM_GESTURES_BACK_GESTURE_BACK_GESTURE_EVENT_HANDLER_H_
